#include "../UberLame_src/NewFix.h"
#include "../UberLame_src/CallStack.h"
#include <vector>
#include <string>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <conio.h>
#include "../UberLame_src/Integer.h"
#include "../UberLame_src/MinMax.h"
#include "../UberLame_src/StlUtils.h"
#include "../UberLame_src/Mersene.h"
#include "../UberLame_src/Vector.h"
#include "../UberLame_src/Timer.h"

#include "SMO.h"

float f_rand()
{
	return float(rand()) / RAND_MAX;
}

Vector3f v_RandGroupPoint(Vector3f v_center, float f_radius)
{
	float f_angle = 2 * f_pi * f_rand();
	float f_angle2 = 2 * f_pi * f_rand();
	f_radius *= sqrt(f_rand());
	return v_center + Vector3f(sin(f_angle) * cos(f_angle2),
							   cos(f_angle) * sin(f_angle2),
							   sin(f_angle) * cos(f_angle2)) * f_radius;
}

class CComparePt {
protected:
	Vector3f m_v_ref;

public:
	CComparePt(Vector3f v_ref)
		:m_v_ref(v_ref)
	{}

	inline bool operator ()(Vector3f v_vec) const
	{
		return fabs(v_vec.x - m_v_ref.x) < 1e-4 ||
			   fabs(v_vec.y - m_v_ref.y) < 1e-4 ||
			   fabs(v_vec.z - m_v_ref.z) < 1e-4;
	}
};

int main(int n_arg_num, const char **p_arg_list)
{
	const int n_example_num = 1000;
	const int n_pass_num = 50;
	const double C = .1;
	const bool b_use_linear_kernel = true;

	for(int n_pass = 0; n_pass < n_pass_num; ++ n_pass) {
		CVectorDataModel<float, int> svm_data;
		if(!svm_data.Allocate(n_example_num, 3)) {
			fprintf(stderr, "error: not enough memory for SMO\n");
			return -1;
		}
		std::vector<Vector3f> used_example_list;
		for(int i = 0; i < n_example_num; ++ i) {
			Vector3f v_point;
			for(int n_gen = 0;; ++ n_gen) {
				v_point = (i % 2)? v_RandGroupPoint(Vector3f(-.5f, -.5f, -.5f), .5f) :
					v_RandGroupPoint(Vector3f(.5f, .5f, .5f), .5f);
				v_point = v_point * .5f + .5f; // normalize to [0, 1]
				if(std::find_if(used_example_list.begin(), used_example_list.end(),
				   CComparePt(v_point)) == used_example_list.end()) {
					used_example_list.push_back(v_point);
					break;
				} else if(n_gen > 10000) {
					fprintf(stderr, "error: it's impossible to stuff %d "
						"different points into cloud this small\n", n_example_num);
					return -1;
				}
			}
			svm_data.Set_Point(i, 0, v_point.x);
			svm_data.Set_Point(i, 1, v_point.y);
			svm_data.Set_Point(i, 2, v_point.z);
			svm_data.Set_Target(i, (i % 2)? +1 : -1);
			// give equal amount of positive and negative examples
		}
		printf("learning (pass %d of %d) ...\n", n_pass + 1, n_pass_num);

		CSMO smo;
		if(!smo.SetModel(&svm_data)) {
			fprintf(stderr, "error: CSMO::SetModel() failed (not enough memory)\n");
			return -1;
		}
		if(!smo.Train(C, 1e-3f, 1e-3f, 15000, true))
			fprintf(stderr, "error: SMO failed to train SVM in maximal number of passes\n");

		float f_err = smo.f_ErrorRate();
		printf("SVM learned %.2f%% of training examples \n", (1 - f_err) * 100);
		// dump error rate on training examples
	}

	return 0;
}
